package cn.good.yan.service.t7;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
 * 7. 原子类
 * 模拟演示 50个线程，模拟100W的累积，所消耗的时间
 * 模拟1 - synchronized 实现的原子操作
 * 模拟2 - AtomicLong 实现的原子操作
 * 模拟3 - LongAdder 实现的原子操作
 * 模拟4 - LongAccumulator 实现的原子操作
 * 结论得出：LongAccumulator 、LongAdder 效率 比 AtomicLong 比 synchronized 在高并发情况下，更快
 *
 * 为什么更快：
 * AtomicLong为什么快：因为底层使用：父类是：Striped64，它的理念是：分散热点，他有一个数组，把全部自旋分散给其他的Cell的数组节点上，减少自旋的时间
 *
 * @author shengren.yan
 * @create 2024-04-02
 */
public class AtomicDemo7 {

    public static final int _1W = 10000;  // 100W次累加
    public static final int threadNumber = 50; // 50个线程

    public static void main(String[] args) throws InterruptedException {

        // 50个线程，每个线程100W次，总点赞数出来
        ClickNumber clickNumber = new ClickNumber();
        long startTime;
        long endTime;
        // 分别开启 对应的线程工厂
        CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);

        // 【模拟1 - synchronized 实现的原子操作】
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * _1W; j++) {
                        // synchronized 的累加方法
                        clickNumber.clickBySynchronized();
                    }
                } finally {
                    countDownLatch1.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t 【synchronized】的方法: " + clickNumber.number);


        // 【模拟2 - AtomicLong 实现的原子操作】
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * _1W; j++) {
                        // AtomicLong 的累加方法
                        clickNumber.clickByAtomicLong();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t 【AtomicLong】的方法: " + clickNumber.atomicLong.get());


        // 【模拟3 - LongAdder 实现的原子操作】
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * _1W; j++) {
                        // LongAdder 的累加方法
                        clickNumber.clickByLongAdder();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t 【LongAdder】的方法: " + clickNumber.longAdder.sum());


        // 【模拟4 - LongAccumulator 实现的原子操作】
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * _1W; j++) {
                        // LongAccumulator 的累加方法
                        clickNumber.clickByLongAccumulator();
                    }
                } finally {
                    // LongAccumulator
                    countDownLatch4.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t 【LongAccumulator】的方法: " + clickNumber.longAccumulator.get());

    }

}


// 资源类 ： 提供4个方法，都是累加的方法
class ClickNumber {
    int number = 0;

    // 模拟1：synchronized 的方法
    public synchronized void clickBySynchronized() {
        number++;
    }

    // 模拟2：AtomicLong 的方法
    AtomicLong atomicLong = new AtomicLong(0);

    public void clickByAtomicLong() {
        atomicLong.getAndIncrement();
    }

    // 模拟3：LongAdder 的方法
    LongAdder longAdder = new LongAdder();

    public void clickByLongAdder() {
        longAdder.increment();
    }

    // 模拟4：LongAccumulator 的方法
    LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);

    public void clickByLongAccumulator() {
        longAccumulator.accumulate(1);
    }

}